This exercise was created for a workshop held on Feb 1st at the annual conference for the International Society of Disease Surveillance on obtaining and examining Wildfire Smoke data. The GitHub Repository with this code can be found at .

Workflow

To perform this analysis we’ll be combining population data with wildfire smoke data (available daily) in order to approximate where, and how many people on the ground might have been exposed. Finally we’ll consider some population characteristics to identify disparate burdens to vulnerable populations.

Let’s begin by loading some selected libraries that we’ll use in this workshop. There are three included here:

  1. simple features for preforming spatial operations
  2. tidyverse partly out of habit, but ever useful for wrangling and visualizing data
  3. data table for speeding up the process of querying and manipulating large data sets
# install.packages(c("sf","tidyverse","date.table"))
library(sf)
library(tidyverse)
library(data.table)

Population Data

To know where people live and how many live there, we’ll need the population data for our state. For this we’ll use the US Census state population centers - these are population-weight centers to each tract in the state. For California we use the state FIPS code ‘06’, you can change the code at the end of the URL below to use your state, and we’ll define it explicitly so we can call on it later. We’ll create the URL on the fly using the paste0 function. Note: fread is the default reader from the data.table package and it allows us to read in this text file directly from a url.

stateFIPs <- "06"
TRurl <- paste0("https://www2.census.gov/geo/docs/reference/cenpop2010/tract/CenPop2010_Mean_TR",stateFIPs,".txt")
TRcenters <- fread(TRurl)

Let’s take a look at the data

TRcenters

Note that when reading in the data R automatically translated the FIPS codes to numbers and leading zeros were dropped

class(TRcenters$STATEFP)
[1] "integer"

Alternatively we may wish to read in the geography FIPS as characters and combine them into a single string to more easily match them up with other data later. We’ll do this by defining the class of the columns as character using colClasses within the fread function. Then combining those fields without separators, using paste0. The %>% operator is used with the tidyverse for piping commands, and mutate appends the dataset with new variables that we define - in this case a long identifier for the tract called GEOID.

TRcentersAlt <- fread(TRurl, 
                      colClasses = c("character",
                                     "character",
                                     "character",
                                     "integer",
                                     "double",
                                     "double")) %>%
                mutate(GEOID = paste0(STATEFP, COUNTYFP, TRACTCE))

Let’s see how that looks

TRcentersAlt
class(TRcentersAlt$STATEFP)
[1] "character"

Now we have a table of tract info, but we need it to be a spatial object if we are going to be able to combine it with smoke information. We use the sf package for this. We’ll use the Lat and Lon field already in the data and assign it the Coordinate Reference System (CRS) corresponding to WGS84. Finally, we can plot the points and show population to verify that everything worked.

TRcentersSP <- st_as_sf(TRcenters,
                        coords = c("LONGITUDE", "LATITUDE"),
                        crs = 4326) 

plot(TRcentersSP["POPULATION"])

Wildfire Smoke Data

There are many potential sources for such data including monitor data, modeled data, and satellite (remotely sensed) data. For this exercise we will be looking at data from the National Oceanic and Atmospheric Administration’s Office of Satellite and Product Operations. Specifically, NOAA’s Hazard Mapping System (HMS) Smoke Product.

Technical Documentation of the HMS Products

Here is a recent paper looking at the 2015 wildfire season in California which found * Cardiovascular and Cerebrovascular Emergency Department Visits Associated With Wildfire Smoke Exposure in California in 2015


Let’s first look at the recent (6 month) archive of HMS data available to download. It can be found here https://satepsanone.nesdis.noaa.gov/pub/FIRE/HMS/GIS/ARCHIVE/. You can see that the first date for which smoke data are available is in July 2018. There were several large fires in California this summer and fall and we’ll be able to examine them with this 6 month archive (keep in mind if accessing this exercise after the Feb 1st ISDS Workshop the available dates will be different).

When looking at the archive, you can see that files for each day are in the ESRI shapefile format, where each day’s spatial data consists of 3 files. In this chunk we’ll download the files (.dbf,.shp, .shx) and read them in as a single spatial object. We use the base function substr to separate the full ‘day’ into parts that we may want to use later. Finally, we’ll look at the fields in the data and produce a simple plot of it.

HMSday <- 20180801 # define the day in the format it appears in the  
  
year <- substr(HMSday, 1,4)
month <- substr(HMSday, 5,6)
day <- substr(HMSday, 7,8)

directory <- tempdir() # create a temporary directory to save the files as they're downloaded. 
  
setwd(directory) # set the working directory to the temp directory we just created
shpFiles <- list(paste0("http://satepsanone.nesdis.noaa.gov/pub/FIRE/HMS/GIS/ARCHIVE/hms_smoke",HMSday,".dbf"),
                 paste0("http://satepsanone.nesdis.noaa.gov/pub/FIRE/HMS/GIS/ARCHIVE/hms_smoke",HMSday,".shp"),
                 paste0("http://satepsanone.nesdis.noaa.gov/pub/FIRE/HMS/GIS/ARCHIVE/hms_smoke",HMSday,".shx"))

shpNames <- list("HMSGIS.dbf","HMSGIS.shp","HMSGIS.shx")
  
GISfiles <- mapply(download.file, # the function to execute
                   shpFiles, # a list of the urls to download
                   shpNames, # a list of the names of the destinations for the downloaded files
                   MoreArgs = list(method = "auto", mode = "wb")) # additional arguments for download.file function
  
Smk <- st_read(dsn = "HMSGIS.shp") %>%  st_set_crs(4326) # read in the shapefile and 
Smk

There are 5 fields in the shapefile. Density is the field that contains the information about the concentration of the smoke observed. We’ll use that to take a preliminary look at the data. Before we plot it, we’ll reassign the values the HMS uses for Density to labels that make more sense for us.


Smk <- Smk %>%
  mutate(year = substr(HMSday, 1,4),
        month = substr(HMSday, 5,6),
        day = substr(HMSday, 7,8),
        date = paste0(year, month, day),
        smoke = factor(ifelse(Density %in% c(5, "5.000"), "light", 
                             ifelse(Density %in% c(27, "27.000") ,"heavy", 
                                    ifelse(Density %in% c(16, "16.000"), "medium", "missing"))),
                levels=c("light","medium","heavy","missing"))
         )

 plot(Smk["smoke"], 
      axes = TRUE, # include axes labels in the plot
      key.pos = 1) # place legend at the bottom

The last thing left to do is to combine the population data and the smoke information. This is a spatial operation, where we want to know which smoke plumes are over the population centers that we first brought in. To do this we’ll use the st_intersection function within the sf package. We’ll also use a special function from the lwgeom package to ensure the the smoke polygons are valid and so that we do not encounter unnecessary errors. The out

# install.packages("lwgeom")
library(lwgeom)

SmkTractDayInt <- st_intersection(st_make_valid(Smk),
                                  TRcentersSP) %>%
                        select(date,
                               year,
                               month,
                               day,
                               smoke,
                               STATEFP,
                               COUNTYFP,
                               TRACTCE,
                               POPULATION)
SmkTractDayInt

In examining the result we can see that a single tract from our population centers data can appear in the result multiple times, either for the same smoke level or for different smoke categories. This is because plumes in the HMS data can be stored as separate shapes, depending on the density of the smoke and the time/satellite from which it was derived. Conversely, some tracts do not appear in the result at all. We’ll deal with that in a moment. For now, we’ll make the decision here to consider an intersection with a plume to be an exposure for that tract and day. This will help to simplify the data and give us one observation for each tract-day-smoke combination in our result.

      
st_geometry(SmkTractDayInt) <- NULL # remove the spatial information for the moment and if we want to work with it we can comment this line out
  
singleDay <- SmkTractDayInt  %>%
                mutate(yn = 1) %>% # since every observataion in our result is an instance of smoke with people, we'll give them all a value of 1
                group_by(date,
                         year,
                         month,
                         day,
                         smoke,
                         STATEFP,
                         COUNTYFP,
                         TRACTCE,
                         POPULATION) %>% # then we'll group by these variables 
                summarise(yn = mean(yn)) %>% # and our yes/no marker will be averaged for instances of common smoke categories
                spread(key = smoke,value = yn)  # lastly, let's restructure the data to a wider format, where each smoke category has its own column
                                                # this is not exactly tidy-compliant, but we'll be using this soon
                
unlink(paste0(tempdir(),'/*'))     # clear temp folder
      

Now we have a dataset for a single day and state, where we could quickly compute the number of people under different smoke categories on a single day.


sum(filter(singleDay, heavy == 1)$POPULATION)  # here is that quick calculation using the tidyr notation
[1] 435066
as.data.table(singleDay)[heavy ==1, sum(POPULATION)] # here is that quick calculation using the data.table notation
[1] 435066

Alternatively, we may want to look at which places have the most people exposed, or view all of the tracts under heavy smoke on this day.


filter(singleDay, heavy ==1) %>% arrange(POPULATION)
NA

Wow, a lot of different counties in California had thousands of people exposed on this day.

Recall that when we intersected the tract centroids and the smoke layer we lost tracts with no smoke on that day. It may also be useful to have these, if to only make it easier to see the entire state and population centers when mapping. But we may also want to quantify the percent of the total state or county population exposed. To do this we’d like for the result of our intersection to contain some information for every combination of date-tract-smoke.

singleDayFull <- left_join(TRcenters, singleDay) %>% 
                replace_na(list(date = HMSday, 
                                year = substr(HMSday, 1,4),
                                month = substr(HMSday, 5,6),
                                day = substr(HMSday, 7,8),
                                light = 0, 
                                medium = 0, 
                                heavy = 0))
Joining, by = c("STATEFP", "COUNTYFP", "TRACTCE", "POPULATION")

Now we have a table of the full list of our state’s tracts, the number of people in that tract, and the smoke level they experienced on that day. This is all well and good, but we’d like to make use of computational power to iterate and look at an episode or entire fire season. We already have the commands that we need, we only have to put them into a function that we can apply to several dates or states. The script in this function is just copied (and adapted slightly) from the chunks above.


processHMSday <- function(HMSday){
  
    year <- substr(HMSday, 1,4)
    month <- substr(HMSday, 5,6)
    day <- substr(HMSday, 7,8)
    
    directory <- tempdir() # create a temporary directory to save the files as they're downloaded. 
      
    setwd(directory) # set the working directory to the temp directory we just created
  
    shpFiles <- list(paste0("http://satepsanone.nesdis.noaa.gov/pub/FIRE/HMS/GIS/ARCHIVE/hms_smoke",HMSday,".dbf"),
                     paste0("http://satepsanone.nesdis.noaa.gov/pub/FIRE/HMS/GIS/ARCHIVE/hms_smoke",HMSday,".shp"),
                     paste0("http://satepsanone.nesdis.noaa.gov/pub/FIRE/HMS/GIS/ARCHIVE/hms_smoke",HMSday,".shx"))
  
    shpNames <- list("HMSGIS.dbf","HMSGIS.shp","HMSGIS.shx")
      
    GISfiles <- mapply(download.file, # the function to execute
                       shpFiles, # a list of the urls to download
                       shpNames, # a list of the names of the destinations for the downloaded files
                       MoreArgs = list(method = "auto", mode = "wb")) # additional arguments for download.file function
      
    Smk <- st_read(dsn = "HMSGIS.shp") %>%  
      st_set_crs(4326) %>%
      mutate(year = substr(HMSday, 1,4),
        month = substr(HMSday, 5,6),
        day = substr(HMSday, 7,8),
        date = paste0(year, month, day),
        smoke = factor(ifelse(Density %in% c(5, "5.000"), "light", 
                             ifelse(Density %in% c(27, "27.000") ,"heavy", 
                                    ifelse(Density %in% c(16, "16.000"), "medium", "missing"))),
                levels=c("light","medium","heavy","missing")))
    
    SmkTractDayInt <- st_intersection(st_make_valid(Smk),
                                      TRcentersSP) %>%
                      select(date,
                             year,
                             month,
                             day,
                             smoke,
                             STATEFP,
                             COUNTYFP,
                             TRACTCE,
                             POPULATION) %>%
                    mutate(yn = 1) %>% 
                    group_by(date,
                             year,
                             month,
                             day,
                             smoke,
                             STATEFP,
                             COUNTYFP,
                             TRACTCE,
                             POPULATION) %>% 
                    summarise(yn = mean(yn)) %>% 
                    spread(key = smoke,value = yn) 
    
    st_geometry(SmkTractDayInt) <- NULL
    
    singleFull <- left_join(TRcenters, SmkTractDayInt) %>% 
                        replace_na(list(date = HMSday, 
                                        year = substr(HMSday, 1,4),
                                        month = substr(HMSday, 5,6),
                                        day = substr(HMSday, 7,8),
                                        light = 0, 
                                        medium = 0, 
                                        heavy = 0))
                    
    unlink(paste0(tempdir(),'/*'))
    
    return(singleFull)
    
}

Now we can run the function for a list of dates and combine them to have a larger table of smoke. We can do this for the first week of the Camp Fire which started on Nov 8, 2018.


From the original tract population centers used earlier we have the total (2010) populations in each tract that we can use to produce estimates of the number of people exposed to heavy wildfire smoke. It may also be useful to understand the vulnerable populations that may be particularly impacted in a single event. For this we can dynamically obtain this data on the fly using the American Community Survey API for 2016. You can see the full list of 2016 ACS variables available through the API here https://api.census.gov/data/2016/acs/acs5/variables.html.

Recall that this study of the 2015 California Wildfire Season found significantly elevated risk of stroke and heart attack for those over 65 one day after exposure to heavy wildfire smoke (using HMS). Let’s begin by grabbing this data from ACS. Notice that a new package is needed to read in the data from the ACS’s API in JSON format.

# install.packages("jsonlite")
library(jsonlite)

variable <- "B09020_001E" # this is a placeholde for the ACS varibale we'll read in, t in this case population over 65
# "B17021_016E" # in poverty and living alone


# first read in the data from the ACS and store as a data frome
#examining the data will show that the first row is the names of the data
elderly <-
    as.data.frame(fromJSON(
      paste0("https://api.census.gov/data/2016/acs/acs5?get=NAME,",variable,"&for=tract:*&in=state:",stateFIPs,"%20county:*"
      ), flatten = T
    )) 

# elderlyName <- fromJSON(paste0("https://api.census.gov/data/2016/acs/acs5/variables/",variable,".json"), flatten = T)$label

# here we can elimiate the uneccessary first row and column AND...
# change the name and type of the variables to match our table of smoke exposures
elderly <- elderly[-1,-1] %>% mutate(estimate = as.integer(as.character(V2)), 
                               STATEFP = as.integer(as.character(V3)),
                               COUNTYFP = as.integer(as.character(V4)),
                               TRACTCE = as.integer(as.character(V5))) %>%
                        select(STATEFP, COUNTYFP, TRACTCE, estimate)

elderly
NA

Next we’ll combine the smoke and vulnerable population data we just grabbed from the ACS. And begin to visualize them.

elderlySmoke <- merge(campFire, elderly)

To begin visualizing the data, let’s first produce a timeline of the number of people exposed. We’ll summarize the data by day.

elderlySmoke %>%
  group_by(day) %>%
  summarise(lightPDs = sum(light*POPULATION, na.rm = T),
            mediumPDs = sum(medium*POPULATION, na.rm = T),
            heavyPDs = sum(heavy*POPULATION, na.rm = T)
  ) %>%
  gather(lightPDs, mediumPDs, heavyPDs, key = smoke, value = PersonDays) %>%
  mutate(smoke = factor(x = smoke, levels = c("lightPDs", "mediumPDs", "heavyPDs"))) %>%
       ggplot(aes(
         x = day,
         y = PersonDays/1000000, 
         fill = smoke
       )) +
       geom_bar(stat="identity", position="dodge") + 
       scale_fill_manual(values = c("yellow","orange","red")) +
       ylab("Millions of Persons Exposed") + 
       xlab("Nov day") 

NA
NA

We could also focus on the elderly exposed over the same period

elderlySmoke %>%
  group_by(day) %>%
  summarise(heavyUnder65= sum(heavy*(POPULATION-estimate), na.rm = T),
            heavyOver65 = sum(heavy*estimate, na.rm = T)
  ) %>%
  gather(heavyUnder65, heavyOver65, key = smoke, value = PersonDays) %>%
  mutate(smoke = factor(x = smoke, levels = c("heavyUnder65", "heavyOver65"))) %>%
       ggplot(aes(
         x = day,
         y = PersonDays/1000000, 
         fill = smoke
       )) +
       geom_bar(stat="identity", position="stack") + 
       scale_fill_manual(values = c("pink","red")) +
       ylab("Millions of Persons Exposed") + 
       xlab("Nov day") 

NA
NA

Another really useful way to look at the data will be on a map. For this we’ll summarize the data by location. Also, where we used plot previously this was fine for getting a sense of the information but not very useful for actually interacting with it. Here, we’ll implement a package that uses the java script library Leaflet. This will produce a map that is much more familiar to web users.

# install.packages("leaflet")
library(leaflet)

# first sumarize the data we want by location, aggregating person days of exposure by tract

mapData <- elderlySmoke %>%
  group_by(STATEFP, COUNTYFP, TRACTCE, LATITUDE, LONGITUDE) %>%
  summarise(ExposedPOP = sum(heavy*POPULATION, na.rm = T),
            ExposedPOP65 = sum(heavy*estimate, na.rm = T)
  )

# define the palette for the results showing full population 
palPOP <- colorQuantile(palette = "Blues", 
                        domain = mapData$ExposedPOP, 
                        n = 3)

# define the palette to show the elderly segment of the population 
pal65 <- colorQuantile(palette = "Reds", 
                        domain = mapData$ExposedPOP65, 
                        n = 3)


# here we create the Leaft map with two layers, one for all ages and one for elderly
  mapData %>%
    leaflet() %>%
      addProviderTiles(providers$CartoDB.Positron) %>% # these are the tiles for the background of the map - find more here http://leaflet-extras.github.io/leaflet-providers/preview/index.html 
      addCircleMarkers( # here we add the first layer, since our data has lat and lon, we can add the tabular data direcetly to a map
        radius = 3,
        color = ~ palPOP(ExposedPOP),
        stroke = FALSE,
        fillOpacity = .3,
        label = ~ExposedPOP, # here is text that will appear on mouseover
        popup = ~ExposedPOP, # this is text to appear on a click
        group = "All Ages"
      ) %>%  
      addCircleMarkers( # here we add the second layer, since our data has lat and lon, we can add the tabular data direcetly to a map
        radius =  ~ ExposedPOP65/max(mapData$ExposedPOP65)*10,
        color = ~ pal65(ExposedPOP65),
        stroke = FALSE,
        fillOpacity = .3,
        label = ~paste0(format(ExposedPOP65, big.mark = ","), " over 65 person-days of exposure in the Week the Camp Fire began"),
        popup = ~ExposedPOP65,
        group = "Over 65"
      ) %>%  
    addLayersControl(
                    baseGroups =  c("All Ages","Over 65"),
                    options = layersControlOptions(collapsed = FALSE)
                ) %>%
      setView(lat = 37.085206, lng = -119.540085, zoom = 6) %>% #defaults the view of the original map to show the entire state
      addEasyButton(easyButton(
        icon="fa-globe", title="Zoom to State",
        onClick=JS("function(btn, map){ map.setView([37.085206, -119.540085],6); }"))) # adds a button to return to the whole staet view
Assuming "LONGITUDE" and "LATITUDE" are longitude and latitude, respectively
Assuming "LONGITUDE" and "LATITUDE" are longitude and latitude, respectively

NA
NA

Please take a moment to provide us with feedback on this session.

Also, try some modifications and exercises on your own. Modify the code to * look at your own state * a specific county * other vulnerable segments of the population * other episodes of wildfire smoke * map more than two layers on ‘exposure’

LS0tDQp0aXRsZTogIlByb2Nlc3NpbmcgSE1TIFdpbGRmaXJlIFNtb2tlIERhdGEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIGV4ZXJjaXNlIHdhcyBjcmVhdGVkIGZvciBhIHdvcmtzaG9wIGhlbGQgb24gRmViIDFzdCBhdCB0aGUgYW5udWFsIGNvbmZlcmVuY2UgZm9yIHRoZSBbSW50ZXJuYXRpb25hbCBTb2NpZXR5IG9mIERpc2Vhc2UgU3VydmVpbGxhbmNlXSgpIG9uIG9idGFpbmluZyBhbmQgZXhhbWluaW5nIFdpbGRmaXJlIFNtb2tlIGRhdGEuIFRoZSBHaXRIdWIgUmVwb3NpdG9yeSB3aXRoIHRoaXMgY29kZSBjYW4gYmUgZm91bmQgYXQgW10oKS4NCg0KIyMjIyBXb3JrZmxvdw0KDQpUbyBwZXJmb3JtIHRoaXMgYW5hbHlzaXMgd2UnbGwgYmUgY29tYmluaW5nIHBvcHVsYXRpb24gZGF0YSB3aXRoIHdpbGRmaXJlIHNtb2tlIGRhdGEgKGF2YWlsYWJsZSBkYWlseSkgaW4gb3JkZXIgdG8gYXBwcm94aW1hdGUgd2hlcmUsIGFuZCBob3cgbWFueSBwZW9wbGUgb24gdGhlIGdyb3VuZCBtaWdodCBoYXZlIGJlZW4gZXhwb3NlZC4gRmluYWxseSB3ZSdsbCBjb25zaWRlciBzb21lIHBvcHVsYXRpb24gY2hhcmFjdGVyaXN0aWNzIHRvIGlkZW50aWZ5IGRpc3BhcmF0ZSBidXJkZW5zIHRvIHZ1bG5lcmFibGUgcG9wdWxhdGlvbnMuIA0KDQpMZXQncyBiZWdpbiBieSBsb2FkaW5nIHNvbWUgc2VsZWN0ZWQgbGlicmFyaWVzIHRoYXQgd2UnbGwgdXNlIGluIHRoaXMgd29ya3Nob3AuIFRoZXJlIGFyZSB0aHJlZSBpbmNsdWRlZCBoZXJlOiANCg0KMS4gIFtzaW1wbGUgZmVhdHVyZXNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9zZi9pbmRleC5odG1sKSBmb3IgcHJlZm9ybWluZyBzcGF0aWFsIG9wZXJhdGlvbnMgDQogICAgKyAqW1RoaXMgaXMgYSBnb29kIHJlc291cmNlXShodHRwczovL2dlb2NvbXByLnJvYmlubG92ZWxhY2UubmV0LykgZm9yIHBlcmZvcm1pbmcgc3BhdGlhbCBvcGVyYXRpb25zIGluIFIqDQoyLiBbdGlkeXZlcnNlXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLykgcGFydGx5IG91dCBvZiBoYWJpdCwgYnV0IGV2ZXIgdXNlZnVsIGZvciB3cmFuZ2xpbmcgYW5kIHZpc3VhbGl6aW5nIGRhdGEgDQogICAgKyAqSSBhbHNvIGhpZ2hseSByZWNvbW1lbmQgW3RoZSBhY2NvbXBhbnlpbmcgYm9va10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei8pKg0KMy4gW2RhdGEgdGFibGVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9kYXRhLnRhYmxlL2luZGV4Lmh0bWwpIGZvciBzcGVlZGluZyB1cCB0aGUgcHJvY2VzcyBvZiBxdWVyeWluZyBhbmQgbWFuaXB1bGF0aW5nIGxhcmdlIGRhdGEgc2V0cw0KICAgICsgKlRoaXMgaXMgYSBnb29kIFtzZXJpZXMgb2YgaW50cm9kdWN0b3J5IHZpZ25ldHRlc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2RhdGEudGFibGUvdmlnbmV0dGVzL2RhdGF0YWJsZS1pbnRyby5odG1sKSB0byB0aGUgcGFja2FnZSoNCg0KYGBge3IscmVzdWx0cz0naGlkZSd9DQojIGluc3RhbGwucGFja2FnZXMoYygic2YiLCJ0aWR5dmVyc2UiLCJkYXRlLnRhYmxlIikpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGRhdGEudGFibGUpDQpgYGANCg0KIyMjIyBQb3B1bGF0aW9uIERhdGENCiANClRvIGtub3cgd2hlcmUgcGVvcGxlIGxpdmUgYW5kIGhvdyBtYW55IGxpdmUgdGhlcmUsIHdlJ2xsIG5lZWQgdGhlIHBvcHVsYXRpb24gZGF0YSBmb3Igb3VyIHN0YXRlLiAgRm9yIHRoaXMgd2UnbGwgdXNlIHRoZSBbVVMgQ2Vuc3VzIHN0YXRlIHBvcHVsYXRpb24gY2VudGVyc10oaHR0cHM6Ly93d3cuY2Vuc3VzLmdvdi9nZW8vcmVmZXJlbmNlL2NlbnRlcnNvZnBvcC5odG1sKSAtIHRoZXNlIGFyZSBwb3B1bGF0aW9uLXdlaWdodCBjZW50ZXJzIHRvIGVhY2ggdHJhY3QgaW4gdGhlIHN0YXRlLiBGb3IgQ2FsaWZvcm5pYSB3ZSB1c2UgdGhlIHN0YXRlIEZJUFMgY29kZSAnMDYnLCB5b3UgY2FuIGNoYW5nZSB0aGUgY29kZSBhdCB0aGUgZW5kIG9mIHRoZSBVUkwgYmVsb3cgdG8gdXNlIHlvdXIgc3RhdGUsIGFuZCB3ZSdsbCBkZWZpbmUgaXQgZXhwbGljaXRseSBzbyB3ZSBjYW4gY2FsbCBvbiBpdCBsYXRlci4gV2UnbGwgY3JlYXRlIHRoZSBVUkwgb24gdGhlIGZseSB1c2luZyB0aGUgYHBhc3RlMGAgZnVuY3Rpb24uIE5vdGU6IGBmcmVhZGAgaXMgdGhlIGRlZmF1bHQgcmVhZGVyIGZyb20gdGhlIGBkYXRhLnRhYmxlYCBwYWNrYWdlIGFuZCBpdCBhbGxvd3MgdXMgdG8gcmVhZCBpbiB0aGlzIHRleHQgZmlsZSBkaXJlY3RseSBmcm9tIGEgdXJsLg0KDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30NCnN0YXRlRklQcyA8LSAiMDYiDQpUUnVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3Mi5jZW5zdXMuZ292L2dlby9kb2NzL3JlZmVyZW5jZS9jZW5wb3AyMDEwL3RyYWN0L0NlblBvcDIwMTBfTWVhbl9UUiIsc3RhdGVGSVBzLCIudHh0IikNClRSY2VudGVycyA8LSBmcmVhZChUUnVybCkNCmBgYA0KDQpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgZGF0YSANCmBgYHtyfQ0KVFJjZW50ZXJzDQpgYGANCg0KTm90ZSB0aGF0IHdoZW4gcmVhZGluZyBpbiB0aGUgZGF0YSBSIGF1dG9tYXRpY2FsbHkgdHJhbnNsYXRlZCB0aGUgRklQUyBjb2RlcyB0byBudW1iZXJzIGFuZCBsZWFkaW5nIHplcm9zIHdlcmUgZHJvcHBlZA0KDQpgYGB7cn0NCmNsYXNzKFRSY2VudGVycyRTVEFURUZQKQ0KYGBgDQoNCkFsdGVybmF0aXZlbHkgd2UgbWF5IHdpc2ggdG8gcmVhZCBpbiB0aGUgZ2VvZ3JhcGh5IEZJUFMgYXMgY2hhcmFjdGVycyBhbmQgY29tYmluZSB0aGVtIGludG8gYSBzaW5nbGUgc3RyaW5nIHRvIG1vcmUgZWFzaWx5IG1hdGNoIHRoZW0gdXAgd2l0aCBvdGhlciBkYXRhIGxhdGVyLiBXZSdsbCBkbyB0aGlzIGJ5IGRlZmluaW5nIHRoZSBjbGFzcyBvZiB0aGUgY29sdW1ucyBhcyBjaGFyYWN0ZXIgdXNpbmcgYGNvbENsYXNzZXNgIHdpdGhpbiB0aGUgYGZyZWFkYCBmdW5jdGlvbi4gVGhlbiBjb21iaW5pbmcgdGhvc2UgZmllbGRzIHdpdGhvdXQgc2VwYXJhdG9ycywgdXNpbmcgYHBhc3RlMGAuIFRoZSBgJT4lYCBvcGVyYXRvciBpcyB1c2VkIHdpdGggdGhlIHRpZHl2ZXJzZSBmb3IgcGlwaW5nIGNvbW1hbmRzLCBhbmQgYG11dGF0ZWAgYXBwZW5kcyB0aGUgZGF0YXNldCB3aXRoIG5ldyB2YXJpYWJsZXMgdGhhdCB3ZSBkZWZpbmUgLSBpbiB0aGlzIGNhc2UgYSBsb25nIGlkZW50aWZpZXIgZm9yIHRoZSB0cmFjdCBjYWxsZWQgKkdFT0lEKi4gDQpgYGB7ciwgcmVzdWx0cz0naGlkZSd9DQpUUmNlbnRlcnNBbHQgPC0gZnJlYWQoVFJ1cmwsIA0KICAgICAgICAgICAgICAgICAgICAgIGNvbENsYXNzZXMgPSBjKCJjaGFyYWN0ZXIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjaGFyYWN0ZXIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjaGFyYWN0ZXIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJpbnRlZ2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZG91YmxlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZG91YmxlIikpICU+JQ0KICAgICAgICAgICAgICAgIG11dGF0ZShHRU9JRCA9IHBhc3RlMChTVEFURUZQLCBDT1VOVFlGUCwgVFJBQ1RDRSkpDQoNCmBgYA0KDQpMZXQncyBzZWUgaG93IHRoYXQgbG9va3MNCmBgYHtyfQ0KVFJjZW50ZXJzQWx0DQpjbGFzcyhUUmNlbnRlcnNBbHQkU1RBVEVGUCkNCmBgYA0KTm93IHdlIGhhdmUgYSB0YWJsZSBvZiB0cmFjdCBpbmZvLCBidXQgd2UgbmVlZCBpdCB0byBiZSBhIHNwYXRpYWwgb2JqZWN0IGlmIHdlIGFyZSBnb2luZyB0byBiZSBhYmxlIHRvIGNvbWJpbmUgaXQgd2l0aCBzbW9rZSBpbmZvcm1hdGlvbi4gV2UgdXNlIHRoZSBgc2ZgIHBhY2thZ2UgZm9yIHRoaXMuIFdlJ2xsIHVzZSB0aGUgTGF0IGFuZCBMb24gZmllbGQgYWxyZWFkeSBpbiB0aGUgZGF0YSBhbmQgYXNzaWduIGl0IHRoZSBbQ29vcmRpbmF0ZSBSZWZlcmVuY2UgU3lzdGVtXShodHRwczovL21naW1vbmQuZ2l0aHViLmlvL1NwYXRpYWwvY29vcmRpbmF0ZS1zeXN0ZW1zLWluLXIuaHRtbCkgKENSUykgY29ycmVzcG9uZGluZyB0byBbV0dTODRdKGh0dHA6Ly9zcGF0aWFscmVmZXJlbmNlLm9yZy9yZWYvZXBzZy93Z3MtODQvKS4gRmluYWxseSwgd2UgY2FuIHBsb3QgdGhlIHBvaW50cyBhbmQgc2hvdyBwb3B1bGF0aW9uIHRvIHZlcmlmeSB0aGF0IGV2ZXJ5dGhpbmcgd29ya2VkLiANCg0KDQpgYGB7cn0NClRSY2VudGVyc1NQIDwtIHN0X2FzX3NmKFRSY2VudGVycywNCiAgICAgICAgICAgICAgICAgICAgICAgIGNvb3JkcyA9IGMoIkxPTkdJVFVERSIsICJMQVRJVFVERSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgY3JzID0gNDMyNikgDQoNCnBsb3QoVFJjZW50ZXJzU1BbIlBPUFVMQVRJT04iXSkNCg0KYGBgDQoNCiMjIyMgV2lsZGZpcmUgU21va2UgRGF0YQ0KVGhlcmUgYXJlIG1hbnkgcG90ZW50aWFsIHNvdXJjZXMgZm9yIHN1Y2ggZGF0YSBpbmNsdWRpbmcgbW9uaXRvciBkYXRhLCBtb2RlbGVkIGRhdGEsIGFuZCBzYXRlbGxpdGUgKHJlbW90ZWx5IHNlbnNlZCkgZGF0YS4gRm9yIHRoaXMgZXhlcmNpc2Ugd2Ugd2lsbCBiZSBsb29raW5nIGF0IGRhdGEgZnJvbSB0aGUgTmF0aW9uYWwgT2NlYW5pYyBhbmQgQXRtb3NwaGVyaWMgQWRtaW5pc3RyYXRpb24ncyBPZmZpY2Ugb2YgU2F0ZWxsaXRlIGFuZCBQcm9kdWN0IE9wZXJhdGlvbnMuIFNwZWNpZmljYWxseSwgW05PQUEncyBIYXphcmQgTWFwcGluZyBTeXN0ZW1dKGh0dHBzOi8vd3d3Lm9zcG8ubm9hYS5nb3YvUHJvZHVjdHMvbGFuZC9obXMuaHRtbCkgKEhNUykgU21va2UgUHJvZHVjdC4gDQoNCiogVGhlIGluaXRpYWwgSE1TIHByb2R1Y3QgZm9yIHRoZSBjdXJyZW50IGRheSBpcyBjcmVhdGVkIGFuZCB1cGRhdGVkIGJ5IGEgc2F0ZWxsaXRlIGFuYWx5c3Qgcm91Z2hseSBiZXR3ZWVuIDhhbSBhbmQgMTBhbSBFYXN0ZXJuIFRpbWUuIEFmdGVyIDEwYW0sIHRoZSBhbmFseXNpcyBpcyBmaW5lLXR1bmVkIGFzIHRpbWUgcGVybWl0cyBhcyBhZGRpdGlvbmFsIHNhdGVsbGl0ZSBkYXRhIGJlY29tZXMgYXZhaWxhYmxlLiBBcmVhcyBvZiBzbW9rZSBhcmUgYW5hbHl6ZWQgYW5kIGFkZGVkIHRvIHRoZSBhbmFseXNpcyBkdXJpbmcgZGF5bGlnaHQgaG91cnMgYXMgdmlzaWJsZSBzYXRlbGxpdGUgaW1hZ2VyeSBiZWNvbWVzIGF2YWlsYWJsZS4gVGhlIHByb2R1Y3QgaXMgZmluYWxpemVkIGFuZCAnY29tcGxldGVkJyBmb3IgdGhlIGFyY2hpdmUgdGhlIGZvbGxvd2luZyBtb3JuaW5nIC0gZ2VuZXJhbGx5IGJ5IGFyb3VuZCA4MDBhbS4NCiogVGhlIGZpcmUgc2l6ZXMgZGVwaWN0ZWQgaW4gdGhlIHByb2R1Y3QgYXJlIHByaW1hcmlseSBkZXRlcm1pbmVkIGJ5IHRoZSBmaWVsZCBvZiB2aWV3IG9mIHRoZSBzYXRlbGxpdGUgaW5zdHJ1bWVudCwgb3IgdGhlIHJlc29sdXRpb24gb2YgdGhlIGFuYWx5c2lzIHRvb2wuIFRoZXkgc2hvdWxkIG5vdCBiZSB1c2VkIHRvIGVzdGltYXRlIHNwZWNpZmljIGZpcmUgcGVyaW1ldGVycy4NCiogVGhlIGFiaWxpdHkgdG8gZGV0ZWN0IGZpcmVzIGFuZCBzbW9rZSBjYW4gYmUgY29tcHJvbWlzZWQgYnkgbWFueSBmYWN0b3JzLCBpbmNsdWRpbmcgY2xvdWQgY292ZXIsIHRyZWUgY2Fub3B5LCB0ZXJyYWluLCB0aGUgc2l6ZSBvZiB0aGUgZmlyZSBvciBzbW9rZSBwbHVtZSwgdGhlIHRpbWUgb2YgdGhlIGRheSwgZXRjLiBUaGUgc2F0ZWxsaXRlIHNlbnNvcnMgdXNlZCB0byBkZXRlY3QgZmlyZXMgYXJlIHNlbnNpdGl2ZSB0byBoZWF0IHNvdXJjZXMgYW5kIHJlZmxlY3RlZCBzdW5saWdodC4gQW5hbHlzdHMgZG8gdGhlaXIgYmVzdCB0byBkaXN0aW5ndWlzaCBiZXR3ZWVuIGZpcmVzIGFuZCBvdGhlciBoZWF0IHNvdXJjZXMgb3IgaGlnaGx5IHJlZmxlY3RpdmUgc3VyZmFjZXMsIHN1Y2ggYXMgZmFjdG9yaWVzLCBtaW5lcywgZ2FzIGZsYXJlcywgc29sYXIgcGFuZWxzLCBjbG91ZHMsIGV0Yy4gYnV0IHNvbWUgZmFsc2UgZGV0ZWN0cyBkbyBnZXQgaW5jbHVkZWQgaW4gdGhlIGFuYWx5c2lzLg0KDQojIyMjIyBUZWNobmljYWwgRG9jdW1lbnRhdGlvbiBvZiB0aGUgSE1TIFByb2R1Y3RzIA0KKiBbVXNlIG9mIG11bHRpcGxlIHNhdGVsbGl0ZSBzZW5zb3JzIGluIE5PQUEncyBvcGVyYXRpb25hbCBuZWFyIHJlYWwtdGltZSBmaXJlIGFuZCBzbW9rZSBkZXRlY3Rpb24gYW5kIGNoYXJhY3Rlcml6YXRpb24gcHJvZ3JhbV0oaHR0cHM6Ly93d3cucmVzZWFyY2hnYXRlLm5ldC9wcm9maWxlL01hcmtfUnVtaW5za2kvcHVibGljYXRpb24vMjUyNDU2NjM2X1VzZV9vZl9tdWx0aXBsZV9zYXRlbGxpdGVfc2Vuc29yc19pbl9OT0FBJTI3c19vcGVyYXRpb25hbF9uZWFyX3JlYWwtdGltZV9maXJlX2FuZF9zbW9rZV9kZXRlY3Rpb25fYW5kX2NoYXJhY3Rlcml6YXRpb25fcHJvZ3JhbS9saW5rcy81NDk4ZDY3YTBjZjJjNWE3ZTM0MmM2ZTgucGRmKQ0KKiBbRGVzY3JpcHRpb24gYW5kIFZlcmlmaWNhdGlvbiBvZiB0aGUgTk9BQSBTbW9rZSBGb3JlY2FzdGluZyBTeXN0ZW06IFRoZSAyMDA3IEZpcmUgU2Vhc29uXShodHRwczovL2pvdXJuYWxzLmFtZXRzb2Mub3JnL2RvaS9wZGYvMTAuMTE3NS8yMDA4V0FGMjIyMjE2NS4xKQ0KDQpIZXJlIGlzIGEgcmVjZW50IHBhcGVyIGxvb2tpbmcgYXQgdGhlIDIwMTUgd2lsZGZpcmUgc2Vhc29uIGluIENhbGlmb3JuaWEgd2hpY2ggZm91bmQgDQoqIFtDYXJkaW92YXNjdWxhciBhbmQgQ2VyZWJyb3Zhc2N1bGFyIEVtZXJnZW5jeSBEZXBhcnRtZW50IFZpc2l0cyBBc3NvY2lhdGVkIFdpdGggV2lsZGZpcmUgU21va2UgRXhwb3N1cmUgaW4gQ2FsaWZvcm5pYSBpbiAyMDE1XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM2MDE1NDAwL3BkZi9KQUgzLTctZTAwNzQ5Mi5wZGYpDQoNCi0tLS0tLS0tLS0tLQ0KTGV0J3MgZmlyc3QgbG9vayBhdCB0aGUgcmVjZW50ICg2IG1vbnRoKSBhcmNoaXZlIG9mIEhNUyBkYXRhIGF2YWlsYWJsZSB0byBkb3dubG9hZC4gSXQgY2FuIGJlIGZvdW5kIGhlcmUgW2h0dHBzOi8vc2F0ZXBzYW5vbmUubmVzZGlzLm5vYWEuZ292L3B1Yi9GSVJFL0hNUy9HSVMvQVJDSElWRS9dKGh0dHBzOi8vc2F0ZXBzYW5vbmUubmVzZGlzLm5vYWEuZ292L3B1Yi9GSVJFL0hNUy9HSVMvQVJDSElWRS8pLiBZb3UgY2FuIHNlZSB0aGF0IHRoZSBmaXJzdCBkYXRlIGZvciB3aGljaCBzbW9rZSBkYXRhIGFyZSBhdmFpbGFibGUgaXMgaW4gSnVseSAyMDE4LiBUaGVyZSB3ZXJlIHNldmVyYWwgbGFyZ2UgZmlyZXMgaW4gQ2FsaWZvcm5pYSB0aGlzIHN1bW1lciBhbmQgZmFsbCBhbmQgd2UnbGwgYmUgYWJsZSB0byBleGFtaW5lIHRoZW0gd2l0aCB0aGlzIDYgbW9udGggYXJjaGl2ZSAoa2VlcCBpbiBtaW5kIGlmIGFjY2Vzc2luZyB0aGlzIGV4ZXJjaXNlIGFmdGVyIHRoZSBGZWIgMXN0IElTRFMgV29ya3Nob3AgdGhlIGF2YWlsYWJsZSBkYXRlcyB3aWxsIGJlIGRpZmZlcmVudCkuDQoNCldoZW4gbG9va2luZyBhdCB0aGUgYXJjaGl2ZSwgeW91IGNhbiBzZWUgdGhhdCBmaWxlcyBmb3IgZWFjaCBkYXkgYXJlIGluIHRoZSBFU1JJIHNoYXBlZmlsZSBmb3JtYXQsIHdoZXJlIGVhY2ggZGF5J3Mgc3BhdGlhbCBkYXRhIGNvbnNpc3RzIG9mIDMgZmlsZXMuIEluIHRoaXMgY2h1bmsgd2UnbGwgZG93bmxvYWQgdGhlIGZpbGVzICgqLmRiZiosKi5zaHAqLCAqLnNoeCopIGFuZCByZWFkIHRoZW0gaW4gYXMgYSBzaW5nbGUgc3BhdGlhbCBvYmplY3QuIFdlIHVzZSB0aGUgYmFzZSBmdW5jdGlvbiBgc3Vic3RyYCB0byBzZXBhcmF0ZSB0aGUgZnVsbCAnZGF5JyBpbnRvIHBhcnRzIHRoYXQgd2UgbWF5IHdhbnQgdG8gdXNlIGxhdGVyLiBGaW5hbGx5LCB3ZSdsbCBsb29rIGF0IHRoZSBmaWVsZHMgaW4gdGhlIGRhdGEgYW5kIHByb2R1Y2UgYSBzaW1wbGUgcGxvdCBvZiBpdC4gDQoNCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30NCkhNU2RheSA8LSAyMDE4MDgwMSAjIGRlZmluZSB0aGUgZGF5IGluIHRoZSBmb3JtYXQgaXQgYXBwZWFycyBpbiB0aGUgIA0KICANCnllYXIgPC0gc3Vic3RyKEhNU2RheSwgMSw0KQ0KbW9udGggPC0gc3Vic3RyKEhNU2RheSwgNSw2KQ0KZGF5IDwtIHN1YnN0cihITVNkYXksIDcsOCkNCg0KZGlyZWN0b3J5IDwtIHRlbXBkaXIoKSAjIGNyZWF0ZSBhIHRlbXBvcmFyeSBkaXJlY3RvcnkgdG8gc2F2ZSB0aGUgZmlsZXMgYXMgdGhleSdyZSBkb3dubG9hZGVkLiANCiAgDQpzZXR3ZChkaXJlY3RvcnkpICMgc2V0IHRoZSB3b3JraW5nIGRpcmVjdG9yeSB0byB0aGUgdGVtcCBkaXJlY3Rvcnkgd2UganVzdCBjcmVhdGVkDQoNCnNocEZpbGVzIDwtIGxpc3QocGFzdGUwKCJodHRwOi8vc2F0ZXBzYW5vbmUubmVzZGlzLm5vYWEuZ292L3B1Yi9GSVJFL0hNUy9HSVMvQVJDSElWRS9obXNfc21va2UiLEhNU2RheSwiLmRiZiIpLA0KICAgICAgICAgICAgICAgICBwYXN0ZTAoImh0dHA6Ly9zYXRlcHNhbm9uZS5uZXNkaXMubm9hYS5nb3YvcHViL0ZJUkUvSE1TL0dJUy9BUkNISVZFL2htc19zbW9rZSIsSE1TZGF5LCIuc2hwIiksDQogICAgICAgICAgICAgICAgIHBhc3RlMCgiaHR0cDovL3NhdGVwc2Fub25lLm5lc2Rpcy5ub2FhLmdvdi9wdWIvRklSRS9ITVMvR0lTL0FSQ0hJVkUvaG1zX3Ntb2tlIixITVNkYXksIi5zaHgiKSkNCg0Kc2hwTmFtZXMgPC0gbGlzdCgiSE1TR0lTLmRiZiIsIkhNU0dJUy5zaHAiLCJITVNHSVMuc2h4IikNCiAgDQpHSVNmaWxlcyA8LSBtYXBwbHkoZG93bmxvYWQuZmlsZSwgIyB0aGUgZnVuY3Rpb24gdG8gZXhlY3V0ZQ0KICAgICAgICAgICAgICAgICAgIHNocEZpbGVzLCAjIGEgbGlzdCBvZiB0aGUgdXJscyB0byBkb3dubG9hZA0KICAgICAgICAgICAgICAgICAgIHNocE5hbWVzLCAjIGEgbGlzdCBvZiB0aGUgbmFtZXMgb2YgdGhlIGRlc3RpbmF0aW9ucyBmb3IgdGhlIGRvd25sb2FkZWQgZmlsZXMNCiAgICAgICAgICAgICAgICAgICBNb3JlQXJncyA9IGxpc3QobWV0aG9kID0gImF1dG8iLCBtb2RlID0gIndiIikpICMgYWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIGRvd25sb2FkLmZpbGUgZnVuY3Rpb24NCiAgDQpTbWsgPC0gc3RfcmVhZChkc24gPSAiSE1TR0lTLnNocCIpICU+JSAgc3Rfc2V0X2Nycyg0MzI2KSAjIHJlYWQgaW4gdGhlIHNoYXBlZmlsZSBhbmQgDQoNClNtaw0KYGBgDQoNClRoZXJlIGFyZSA1IGZpZWxkcyBpbiB0aGUgc2hhcGVmaWxlLiAqRGVuc2l0eSogaXMgdGhlIGZpZWxkIHRoYXQgY29udGFpbnMgdGhlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjb25jZW50cmF0aW9uIG9mIHRoZSBzbW9rZSBvYnNlcnZlZC4gV2UnbGwgdXNlIHRoYXQgdG8gdGFrZSBhIHByZWxpbWluYXJ5IGxvb2sgYXQgdGhlIGRhdGEuICBCZWZvcmUgd2UgcGxvdCBpdCwgd2UnbGwgcmVhc3NpZ24gdGhlIHZhbHVlcyB0aGUgSE1TIHVzZXMgZm9yICpEZW5zaXR5KiB0byBsYWJlbHMgdGhhdCBtYWtlIG1vcmUgc2Vuc2UgZm9yIHVzLiANCg0KYGBge3J9DQoNClNtayA8LSBTbWsgJT4lDQogIG11dGF0ZSh5ZWFyID0gc3Vic3RyKEhNU2RheSwgMSw0KSwNCiAgICAgICAgbW9udGggPSBzdWJzdHIoSE1TZGF5LCA1LDYpLA0KICAgICAgICBkYXkgPSBzdWJzdHIoSE1TZGF5LCA3LDgpLA0KICAgICAgICBkYXRlID0gcGFzdGUwKHllYXIsIG1vbnRoLCBkYXkpLA0KICAgICAgICBzbW9rZSA9IGZhY3RvcihpZmVsc2UoRGVuc2l0eSAlaW4lIGMoNSwgIjUuMDAwIiksICJsaWdodCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRGVuc2l0eSAlaW4lIGMoMjcsICIyNy4wMDAiKSAsImhlYXZ5IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRGVuc2l0eSAlaW4lIGMoMTYsICIxNi4wMDAiKSwgIm1lZGl1bSIsICJtaXNzaW5nIikpKSwNCiAgICAgICAgICAgICAgICBsZXZlbHM9YygibGlnaHQiLCJtZWRpdW0iLCJoZWF2eSIsIm1pc3NpbmciKSkNCiAgICAgICAgICkNCg0KIHBsb3QoU21rWyJzbW9rZSJdLCANCiAgICAgIGF4ZXMgPSBUUlVFLCAjIGluY2x1ZGUgYXhlcyBsYWJlbHMgaW4gdGhlIHBsb3QNCiAgICAgIGtleS5wb3MgPSAxKSAjIHBsYWNlIGxlZ2VuZCBhdCB0aGUgYm90dG9tDQoNCmBgYA0KVGhlIGxhc3QgdGhpbmcgbGVmdCB0byBkbyBpcyB0byBjb21iaW5lIHRoZSBwb3B1bGF0aW9uIGRhdGEgYW5kIHRoZSBzbW9rZSBpbmZvcm1hdGlvbi4gVGhpcyBpcyBhIHNwYXRpYWwgb3BlcmF0aW9uLCB3aGVyZSB3ZSB3YW50IHRvIGtub3cgd2hpY2ggc21va2UgcGx1bWVzIGFyZSBvdmVyICB0aGUgcG9wdWxhdGlvbiBjZW50ZXJzIHRoYXQgd2UgZmlyc3QgYnJvdWdodCBpbi4gVG8gZG8gdGhpcyB3ZSdsbCB1c2UgdGhlIGBzdF9pbnRlcnNlY3Rpb25gIGZ1bmN0aW9uIHdpdGhpbiB0aGUgYHNmYCBwYWNrYWdlLiBXZSdsbCBhbHNvIHVzZSBhIHNwZWNpYWwgZnVuY3Rpb24gZnJvbSB0aGUgYGx3Z2VvbWAgcGFja2FnZSB0byBlbnN1cmUgdGhlIHRoZSBzbW9rZSBwb2x5Z29ucyBhcmUgdmFsaWQgYW5kIHNvIHRoYXQgd2UgZG8gbm90IGVuY291bnRlciB1bm5lY2Vzc2FyeSBlcnJvcnMuIFRoZSBvdXQNCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnfQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJsd2dlb20iKQ0KbGlicmFyeShsd2dlb20pDQoNClNta1RyYWN0RGF5SW50IDwtIHN0X2ludGVyc2VjdGlvbihzdF9tYWtlX3ZhbGlkKFNtayksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJjZW50ZXJzU1ApICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KGRhdGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb250aCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc21va2UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU1RBVEVGUCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDT1VOVFlGUCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUkFDVENFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBPUFVMQVRJT04pDQoNClNta1RyYWN0RGF5SW50DQpgYGANCkluIGV4YW1pbmluZyB0aGUgcmVzdWx0IHdlIGNhbiBzZWUgdGhhdCBhIHNpbmdsZSB0cmFjdCBmcm9tIG91ciBwb3B1bGF0aW9uIGNlbnRlcnMgZGF0YSBjYW4gYXBwZWFyIGluIHRoZSByZXN1bHQgbXVsdGlwbGUgdGltZXMsIGVpdGhlciBmb3IgdGhlIHNhbWUgc21va2UgbGV2ZWwgb3IgZm9yIGRpZmZlcmVudCBzbW9rZSBjYXRlZ29yaWVzLiBUaGlzIGlzIGJlY2F1c2UgcGx1bWVzIGluIHRoZSBITVMgZGF0YSBjYW4gYmUgc3RvcmVkIGFzIHNlcGFyYXRlIHNoYXBlcywgZGVwZW5kaW5nIG9uIHRoZSBkZW5zaXR5IG9mIHRoZSBzbW9rZSBhbmQgdGhlIHRpbWUvc2F0ZWxsaXRlIGZyb20gd2hpY2ggaXQgd2FzIGRlcml2ZWQuIENvbnZlcnNlbHksIHNvbWUgdHJhY3RzIGRvIG5vdCBhcHBlYXIgaW4gdGhlIHJlc3VsdCBhdCBhbGwuIFdlJ2xsIGRlYWwgd2l0aCB0aGF0IGluIGEgbW9tZW50LiBGb3Igbm93LCB3ZSdsbCBtYWtlIHRoZSBkZWNpc2lvbiBoZXJlIHRvIGNvbnNpZGVyIGFuIGludGVyc2VjdGlvbiB3aXRoIGEgcGx1bWUgdG8gYmUgYW4gZXhwb3N1cmUgZm9yIHRoYXQgdHJhY3QgYW5kIGRheS4gVGhpcyB3aWxsIGhlbHAgdG8gc2ltcGxpZnkgdGhlIGRhdGEgYW5kIGdpdmUgdXMgb25lIG9ic2VydmF0aW9uIGZvciBlYWNoIHRyYWN0LWRheS1zbW9rZSBjb21iaW5hdGlvbiBpbiBvdXIgcmVzdWx0Lg0KDQoNCmBgYHtyfQ0KICAgICAgDQpzdF9nZW9tZXRyeShTbWtUcmFjdERheUludCkgPC0gTlVMTCAjIHJlbW92ZSB0aGUgc3BhdGlhbCBpbmZvcm1hdGlvbiBmb3IgdGhlIG1vbWVudCBhbmQgaWYgd2Ugd2FudCB0byB3b3JrIHdpdGggaXQgd2UgY2FuIGNvbW1lbnQgdGhpcyBsaW5lIG91dA0KICANCnNpbmdsZURheSA8LSBTbWtUcmFjdERheUludCAgJT4lDQogICAgICAgICAgICAgICAgbXV0YXRlKHluID0gMSkgJT4lICMgc2luY2UgZXZlcnkgb2JzZXJ2YXRhaW9uIGluIG91ciByZXN1bHQgaXMgYW4gaW5zdGFuY2Ugb2Ygc21va2Ugd2l0aCBwZW9wbGUsIHdlJ2xsIGdpdmUgdGhlbSBhbGwgYSB2YWx1ZSBvZiAxDQogICAgICAgICAgICAgICAgZ3JvdXBfYnkoZGF0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1vbnRoLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGRheSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBzbW9rZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBTVEFURUZQLA0KICAgICAgICAgICAgICAgICAgICAgICAgIENPVU5UWUZQLA0KICAgICAgICAgICAgICAgICAgICAgICAgIFRSQUNUQ0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgUE9QVUxBVElPTikgJT4lICMgdGhlbiB3ZSdsbCBncm91cCBieSB0aGVzZSB2YXJpYWJsZXMgDQogICAgICAgICAgICAgICAgc3VtbWFyaXNlKHluID0gbWVhbih5bikpICU+JSAjIGFuZCBvdXIgeWVzL25vIG1hcmtlciB3aWxsIGJlIGF2ZXJhZ2VkIGZvciBpbnN0YW5jZXMgb2YgY29tbW9uIHNtb2tlIGNhdGVnb3JpZXMNCiAgICAgICAgICAgICAgICBzcHJlYWQoa2V5ID0gc21va2UsdmFsdWUgPSB5bikgICMgbGFzdGx5LCBsZXQncyByZXN0cnVjdHVyZSB0aGUgZGF0YSB0byBhIHdpZGVyIGZvcm1hdCwgd2hlcmUgZWFjaCBzbW9rZSBjYXRlZ29yeSBoYXMgaXRzIG93biBjb2x1bW4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhpcyBpcyBub3QgZXhhY3RseSB0aWR5LWNvbXBsaWFudCwgYnV0IHdlJ2xsIGJlIHVzaW5nIHRoaXMgc29vbg0KICAgICAgICAgICAgICAgIA0KdW5saW5rKHBhc3RlMCh0ZW1wZGlyKCksJy8qJykpICAgICAjIGNsZWFyIHRlbXAgZm9sZGVyDQogICAgICANCmBgYA0KTm93IHdlIGhhdmUgYSBkYXRhc2V0IGZvciBhIHNpbmdsZSBkYXkgYW5kIHN0YXRlLCB3aGVyZSB3ZSBjb3VsZCBxdWlja2x5IGNvbXB1dGUgdGhlIG51bWJlciBvZiBwZW9wbGUgdW5kZXIgZGlmZmVyZW50IHNtb2tlIGNhdGVnb3JpZXMgb24gYSBzaW5nbGUgZGF5LiAgDQoNCmBgYHtyfQ0KDQpzdW0oZmlsdGVyKHNpbmdsZURheSwgaGVhdnkgPT0gMSkkUE9QVUxBVElPTikgICMgaGVyZSBpcyB0aGF0IHF1aWNrIGNhbGN1bGF0aW9uIHVzaW5nIHRoZSB0aWR5ciBub3RhdGlvbg0KDQphcy5kYXRhLnRhYmxlKHNpbmdsZURheSlbaGVhdnkgPT0xLCBzdW0oUE9QVUxBVElPTildICMgaGVyZSBpcyB0aGF0IHF1aWNrIGNhbGN1bGF0aW9uIHVzaW5nIHRoZSBkYXRhLnRhYmxlIG5vdGF0aW9uDQoNCmBgYA0KQWx0ZXJuYXRpdmVseSwgd2UgbWF5IHdhbnQgdG8gbG9vayBhdCB3aGljaCBwbGFjZXMgaGF2ZSB0aGUgbW9zdCBwZW9wbGUgZXhwb3NlZCwgb3IgdmlldyBhbGwgb2YgdGhlIHRyYWN0cyB1bmRlciBoZWF2eSBzbW9rZSBvbiB0aGlzIGRheS4gIA0KDQpgYGB7cn0NCg0KZmlsdGVyKHNpbmdsZURheSwgaGVhdnkgPT0xKSAlPiUgYXJyYW5nZShQT1BVTEFUSU9OKQ0KDQpgYGANCldvdywgYSBsb3Qgb2YgZGlmZmVyZW50IGNvdW50aWVzIGluIENhbGlmb3JuaWEgaGFkIHRob3VzYW5kcyBvZiBwZW9wbGUgZXhwb3NlZCBvbiB0aGlzIGRheS4gDQoNClJlY2FsbCB0aGF0IHdoZW4gd2UgaW50ZXJzZWN0ZWQgdGhlIHRyYWN0IGNlbnRyb2lkcyBhbmQgdGhlIHNtb2tlIGxheWVyIHdlIGxvc3QgdHJhY3RzIHdpdGggbm8gc21va2Ugb24gdGhhdCBkYXkuIEl0IG1heSBhbHNvIGJlIHVzZWZ1bCB0byBoYXZlIHRoZXNlLCBpZiB0byBvbmx5IG1ha2UgaXQgZWFzaWVyIHRvIHNlZSB0aGUgZW50aXJlIHN0YXRlIGFuZCBwb3B1bGF0aW9uIGNlbnRlcnMgd2hlbiBtYXBwaW5nLiBCdXQgd2UgbWF5IGFsc28gd2FudCB0byBxdWFudGlmeSB0aGUgcGVyY2VudCBvZiB0aGUgdG90YWwgc3RhdGUgb3IgY291bnR5IHBvcHVsYXRpb24gZXhwb3NlZC4gVG8gZG8gdGhpcyB3ZSdkIGxpa2UgZm9yIHRoZSByZXN1bHQgb2Ygb3VyIGludGVyc2VjdGlvbiB0byBjb250YWluIHNvbWUgaW5mb3JtYXRpb24gZm9yIGV2ZXJ5IGNvbWJpbmF0aW9uIG9mIGRhdGUtdHJhY3Qtc21va2UuIA0KDQpgYGB7cn0NCnNpbmdsZURheUZ1bGwgPC0gbGVmdF9qb2luKFRSY2VudGVycywgc2luZ2xlRGF5KSAlPiUgDQogICAgICAgICAgICAgICAgcmVwbGFjZV9uYShsaXN0KGRhdGUgPSBITVNkYXksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyID0gc3Vic3RyKEhNU2RheSwgMSw0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9udGggPSBzdWJzdHIoSE1TZGF5LCA1LDYpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXkgPSBzdWJzdHIoSE1TZGF5LCA3LDgpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaWdodCA9IDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWRpdW0gPSAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhdnkgPSAwKSkNCg0KYGBgDQoNCk5vdyB3ZSBoYXZlIGEgdGFibGUgb2YgdGhlIGZ1bGwgbGlzdCBvZiBvdXIgc3RhdGUncyB0cmFjdHMsIHRoZSBudW1iZXIgb2YgcGVvcGxlIGluIHRoYXQgdHJhY3QsIGFuZCB0aGUgc21va2UgbGV2ZWwgdGhleSBleHBlcmllbmNlZCBvbiB0aGF0IGRheS4gVGhpcyBpcyBhbGwgd2VsbCBhbmQgZ29vZCwgYnV0IHdlJ2QgbGlrZSB0byBtYWtlIHVzZSBvZiBjb21wdXRhdGlvbmFsIHBvd2VyIHRvIGl0ZXJhdGUgYW5kIGxvb2sgYXQgYW4gZXBpc29kZSBvciBlbnRpcmUgZmlyZSBzZWFzb24uIFdlIGFscmVhZHkgaGF2ZSB0aGUgY29tbWFuZHMgdGhhdCB3ZSBuZWVkLCB3ZSBvbmx5IGhhdmUgdG8gcHV0IHRoZW0gaW50byBhIGZ1bmN0aW9uIHRoYXQgd2UgY2FuIGFwcGx5IHRvIHNldmVyYWwgZGF0ZXMgb3Igc3RhdGVzLiBUaGUgc2NyaXB0IGluIHRoaXMgZnVuY3Rpb24gaXMganVzdCBjb3BpZWQgKGFuZCBhZGFwdGVkIHNsaWdodGx5KSBmcm9tIHRoZSBjaHVua3MgYWJvdmUuIA0KDQpgYGB7cn0NCg0KcHJvY2Vzc0hNU2RheSA8LSBmdW5jdGlvbihITVNkYXkpew0KICANCiAgICB5ZWFyIDwtIHN1YnN0cihITVNkYXksIDEsNCkNCiAgICBtb250aCA8LSBzdWJzdHIoSE1TZGF5LCA1LDYpDQogICAgZGF5IDwtIHN1YnN0cihITVNkYXksIDcsOCkNCiAgICANCiAgICBkaXJlY3RvcnkgPC0gdGVtcGRpcigpICMgY3JlYXRlIGEgdGVtcG9yYXJ5IGRpcmVjdG9yeSB0byBzYXZlIHRoZSBmaWxlcyBhcyB0aGV5J3JlIGRvd25sb2FkZWQuIA0KICAgICAgDQogICAgc2V0d2QoZGlyZWN0b3J5KSAjIHNldCB0aGUgd29ya2luZyBkaXJlY3RvcnkgdG8gdGhlIHRlbXAgZGlyZWN0b3J5IHdlIGp1c3QgY3JlYXRlZA0KICANCiAgICBzaHBGaWxlcyA8LSBsaXN0KHBhc3RlMCgiaHR0cDovL3NhdGVwc2Fub25lLm5lc2Rpcy5ub2FhLmdvdi9wdWIvRklSRS9ITVMvR0lTL0FSQ0hJVkUvaG1zX3Ntb2tlIixITVNkYXksIi5kYmYiKSwNCiAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiaHR0cDovL3NhdGVwc2Fub25lLm5lc2Rpcy5ub2FhLmdvdi9wdWIvRklSRS9ITVMvR0lTL0FSQ0hJVkUvaG1zX3Ntb2tlIixITVNkYXksIi5zaHAiKSwNCiAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiaHR0cDovL3NhdGVwc2Fub25lLm5lc2Rpcy5ub2FhLmdvdi9wdWIvRklSRS9ITVMvR0lTL0FSQ0hJVkUvaG1zX3Ntb2tlIixITVNkYXksIi5zaHgiKSkNCiAgDQogICAgc2hwTmFtZXMgPC0gbGlzdCgiSE1TR0lTLmRiZiIsIkhNU0dJUy5zaHAiLCJITVNHSVMuc2h4IikNCiAgICAgIA0KICAgIEdJU2ZpbGVzIDwtIG1hcHBseShkb3dubG9hZC5maWxlLCAjIHRoZSBmdW5jdGlvbiB0byBleGVjdXRlDQogICAgICAgICAgICAgICAgICAgICAgIHNocEZpbGVzLCAjIGEgbGlzdCBvZiB0aGUgdXJscyB0byBkb3dubG9hZA0KICAgICAgICAgICAgICAgICAgICAgICBzaHBOYW1lcywgIyBhIGxpc3Qgb2YgdGhlIG5hbWVzIG9mIHRoZSBkZXN0aW5hdGlvbnMgZm9yIHRoZSBkb3dubG9hZGVkIGZpbGVzDQogICAgICAgICAgICAgICAgICAgICAgIE1vcmVBcmdzID0gbGlzdChtZXRob2QgPSAiYXV0byIsIG1vZGUgPSAid2IiKSkgIyBhZGRpdGlvbmFsIGFyZ3VtZW50cyBmb3IgZG93bmxvYWQuZmlsZSBmdW5jdGlvbg0KICAgICAgDQogICAgU21rIDwtIHN0X3JlYWQoZHNuID0gIkhNU0dJUy5zaHAiKSAlPiUgIA0KICAgICAgc3Rfc2V0X2Nycyg0MzI2KSAlPiUNCiAgICAgIG11dGF0ZSh5ZWFyID0gc3Vic3RyKEhNU2RheSwgMSw0KSwNCiAgICAgICAgbW9udGggPSBzdWJzdHIoSE1TZGF5LCA1LDYpLA0KICAgICAgICBkYXkgPSBzdWJzdHIoSE1TZGF5LCA3LDgpLA0KICAgICAgICBkYXRlID0gcGFzdGUwKHllYXIsIG1vbnRoLCBkYXkpLA0KICAgICAgICBzbW9rZSA9IGZhY3RvcihpZmVsc2UoRGVuc2l0eSAlaW4lIGMoNSwgIjUuMDAwIiksICJsaWdodCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRGVuc2l0eSAlaW4lIGMoMjcsICIyNy4wMDAiKSAsImhlYXZ5IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRGVuc2l0eSAlaW4lIGMoMTYsICIxNi4wMDAiKSwgIm1lZGl1bSIsICJtaXNzaW5nIikpKSwNCiAgICAgICAgICAgICAgICBsZXZlbHM9YygibGlnaHQiLCJtZWRpdW0iLCJoZWF2eSIsIm1pc3NpbmciKSkpDQogICAgDQogICAgU21rVHJhY3REYXlJbnQgPC0gc3RfaW50ZXJzZWN0aW9uKHN0X21ha2VfdmFsaWQoU21rKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJjZW50ZXJzU1ApICU+JQ0KICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChkYXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb250aCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbW9rZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU1RBVEVGUCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ09VTlRZRlAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSQUNUQ0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBPUFVMQVRJT04pICU+JQ0KICAgICAgICAgICAgICAgICAgICBtdXRhdGUoeW4gPSAxKSAlPiUgDQogICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KGRhdGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vbnRoLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNtb2tlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTVEFURUZQLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDT1VOVFlGUCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJBQ1RDRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUE9QVUxBVElPTikgJT4lIA0KICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UoeW4gPSBtZWFuKHluKSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICBzcHJlYWQoa2V5ID0gc21va2UsdmFsdWUgPSB5bikgDQogICAgDQogICAgc3RfZ2VvbWV0cnkoU21rVHJhY3REYXlJbnQpIDwtIE5VTEwNCiAgICANCiAgICBzaW5nbGVGdWxsIDwtIGxlZnRfam9pbihUUmNlbnRlcnMsIFNta1RyYWN0RGF5SW50KSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlX25hKGxpc3QoZGF0ZSA9IEhNU2RheSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciA9IHN1YnN0cihITVNkYXksIDEsNCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9udGggPSBzdWJzdHIoSE1TZGF5LCA1LDYpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRheSA9IHN1YnN0cihITVNkYXksIDcsOCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlnaHQgPSAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWRpdW0gPSAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWF2eSA9IDApKQ0KICAgICAgICAgICAgICAgICAgICANCiAgICB1bmxpbmsocGFzdGUwKHRlbXBkaXIoKSwnLyonKSkNCiAgICANCiAgICByZXR1cm4oc2luZ2xlRnVsbCkNCiAgICANCn0NCg0KDQpgYGANCg0KTm93IHdlIGNhbiBydW4gdGhlIGZ1bmN0aW9uIGZvciBhIGxpc3Qgb2YgZGF0ZXMgYW5kIGNvbWJpbmUgdGhlbSB0byBoYXZlIGEgbGFyZ2VyIHRhYmxlIG9mIHNtb2tlLiBXZSBjYW4gZG8gdGhpcyBmb3IgdGhlIGZpcnN0IHdlZWsgb2YgdGhlIENhbXAgRmlyZSB3aGljaCBzdGFydGVkIG9uIE5vdiA4LCAyMDE4LiANCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnfQ0KDQpkYXRlTGlzdCA8LSAyMDE4MTEwODoyMDE4MTExNg0KDQpjYW1wRmlyZSA8LSByYmluZGxpc3QobGFwcGx5KGRhdGVMaXN0LCBGVU4gPSBwcm9jZXNzSE1TZGF5KSwgZmlsbCA9IFRSVUUpDQpgYGANCi0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCkZyb20gdGhlIG9yaWdpbmFsIHRyYWN0IHBvcHVsYXRpb24gY2VudGVycyB1c2VkIGVhcmxpZXIgd2UgaGF2ZSB0aGUgdG90YWwgKDIwMTApIHBvcHVsYXRpb25zIGluIGVhY2ggdHJhY3QgdGhhdCB3ZSBjYW4gdXNlIHRvIHByb2R1Y2UgZXN0aW1hdGVzIG9mIHRoZSBudW1iZXIgb2YgcGVvcGxlIGV4cG9zZWQgdG8gaGVhdnkgd2lsZGZpcmUgc21va2UuIEl0IG1heSBhbHNvIGJlIHVzZWZ1bCB0byB1bmRlcnN0YW5kIHRoZSB2dWxuZXJhYmxlIHBvcHVsYXRpb25zIHRoYXQgbWF5IGJlIHBhcnRpY3VsYXJseSBpbXBhY3RlZCBpbiBhIHNpbmdsZSBldmVudC4gRm9yIHRoaXMgd2UgY2FuIGR5bmFtaWNhbGx5IG9idGFpbiB0aGlzIGRhdGEgb24gdGhlIGZseSB1c2luZyB0aGUgW0FtZXJpY2FuIENvbW11bml0eSBTdXJ2ZXkgQVBJXShodHRwczovL3d3dy5jZW5zdXMuZ292L2RhdGEvZGV2ZWxvcGVycy9kYXRhLXNldHMvYWNzLTV5ZWFyLjIwMTYuaHRtbCkgZm9yIDIwMTYuIFlvdSBjYW4gc2VlIHRoZSBmdWxsIGxpc3Qgb2YgMjAxNiBBQ1MgdmFyaWFibGVzIGF2YWlsYWJsZSB0aHJvdWdoIHRoZSBBUEkgaGVyZSBbaHR0cHM6Ly9hcGkuY2Vuc3VzLmdvdi9kYXRhLzIwMTYvYWNzL2FjczUvdmFyaWFibGVzLmh0bWxdKGh0dHBzOi8vYXBpLmNlbnN1cy5nb3YvZGF0YS8yMDE2L2Fjcy9hY3M1L3ZhcmlhYmxlcy5odG1sKS4gDQoNClJlY2FsbCB0aGF0IFt0aGlzIHN0dWR5IG9mIHRoZSAyMDE1IENhbGlmb3JuaWEgV2lsZGZpcmUgU2Vhc29uXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM2MDE1NDAwL3BkZi9KQUgzLTctZTAwNzQ5Mi5wZGYpIGZvdW5kIHNpZ25pZmljYW50bHkgZWxldmF0ZWQgcmlzayBvZiBzdHJva2UgYW5kIGhlYXJ0IGF0dGFjayBmb3IgdGhvc2Ugb3ZlciA2NSBvbmUgZGF5IGFmdGVyIGV4cG9zdXJlIHRvIGhlYXZ5IHdpbGRmaXJlIHNtb2tlICh1c2luZyBITVMpLiBMZXQncyBiZWdpbiBieSBncmFiYmluZyB0aGlzIGRhdGEgZnJvbSBBQ1MuICBOb3RpY2UgdGhhdCBhIG5ldyBwYWNrYWdlIGlzIG5lZWRlZCB0byByZWFkIGluIHRoZSBkYXRhIGZyb20gdGhlIEFDUydzIEFQSSBpbiBKU09OIGZvcm1hdC4NCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoImpzb25saXRlIikNCmxpYnJhcnkoanNvbmxpdGUpDQoNCnZhcmlhYmxlIDwtICJCMDkwMjBfMDAxRSIgIyB0aGlzIGlzIGEgcGxhY2Vob2xkZSBmb3IgdGhlIEFDUyB2YXJpYmFsZSB3ZSdsbCByZWFkIGluLCB0IGluIHRoaXMgY2FzZSBwb3B1bGF0aW9uIG92ZXIgNjUNCiMgIkIxNzAyMV8wMTZFIiAjIGluIHBvdmVydHkgYW5kIGxpdmluZyBhbG9uZQ0KDQoNCiMgZmlyc3QgcmVhZCBpbiB0aGUgZGF0YSBmcm9tIHRoZSBBQ1MgYW5kIHN0b3JlIGFzIGEgZGF0YSBmcm9tZQ0KI2V4YW1pbmluZyB0aGUgZGF0YSB3aWxsIHNob3cgdGhhdCB0aGUgZmlyc3Qgcm93IGlzIHRoZSBuYW1lcyBvZiB0aGUgZGF0YQ0KZWxkZXJseSA8LQ0KICAgIGFzLmRhdGEuZnJhbWUoZnJvbUpTT04oDQogICAgICBwYXN0ZTAoImh0dHBzOi8vYXBpLmNlbnN1cy5nb3YvZGF0YS8yMDE2L2Fjcy9hY3M1P2dldD1OQU1FLCIsdmFyaWFibGUsIiZmb3I9dHJhY3Q6KiZpbj1zdGF0ZToiLHN0YXRlRklQcywiJTIwY291bnR5OioiDQogICAgICApLCBmbGF0dGVuID0gVA0KICAgICkpIA0KDQojIGVsZGVybHlOYW1lIDwtIGZyb21KU09OKHBhc3RlMCgiaHR0cHM6Ly9hcGkuY2Vuc3VzLmdvdi9kYXRhLzIwMTYvYWNzL2FjczUvdmFyaWFibGVzLyIsdmFyaWFibGUsIi5qc29uIiksIGZsYXR0ZW4gPSBUKSRsYWJlbA0KDQojIGhlcmUgd2UgY2FuIGVsaW1pYXRlIHRoZSB1bmVjY2Vzc2FyeSBmaXJzdCByb3cgYW5kIGNvbHVtbiBBTkQuLi4NCiMgY2hhbmdlIHRoZSBuYW1lIGFuZCB0eXBlIG9mIHRoZSB2YXJpYWJsZXMgdG8gbWF0Y2ggb3VyIHRhYmxlIG9mIHNtb2tlIGV4cG9zdXJlcw0KZWxkZXJseSA8LSBlbGRlcmx5Wy0xLC0xXSAlPiUgbXV0YXRlKGVzdGltYXRlID0gYXMuaW50ZWdlcihhcy5jaGFyYWN0ZXIoVjIpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU1RBVEVGUCA9IGFzLmludGVnZXIoYXMuY2hhcmFjdGVyKFYzKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ09VTlRZRlAgPSBhcy5pbnRlZ2VyKGFzLmNoYXJhY3RlcihWNCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSQUNUQ0UgPSBhcy5pbnRlZ2VyKGFzLmNoYXJhY3RlcihWNSkpKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChTVEFURUZQLCBDT1VOVFlGUCwgVFJBQ1RDRSwgZXN0aW1hdGUpDQoNCmVsZGVybHkNCg0KYGBgDQpOZXh0IHdlJ2xsIGNvbWJpbmUgdGhlIHNtb2tlIGFuZCB2dWxuZXJhYmxlIHBvcHVsYXRpb24gZGF0YSB3ZSBqdXN0IGdyYWJiZWQgZnJvbSB0aGUgQUNTLiBBbmQgYmVnaW4gdG8gdmlzdWFsaXplIHRoZW0uIA0KYGBge3J9DQplbGRlcmx5U21va2UgPC0gbWVyZ2UoY2FtcEZpcmUsIGVsZGVybHkpDQoNCmBgYA0KDQpUbyBiZWdpbiB2aXN1YWxpemluZyB0aGUgZGF0YSwgbGV0J3MgZmlyc3QgcHJvZHVjZSBhIHRpbWVsaW5lIG9mIHRoZSBudW1iZXIgb2YgcGVvcGxlIGV4cG9zZWQuIFdlJ2xsIHN1bW1hcml6ZSB0aGUgZGF0YSBieSBkYXkuDQoNCmBgYHtyfQ0KZWxkZXJseVNtb2tlICU+JQ0KICBncm91cF9ieShkYXkpICU+JQ0KICBzdW1tYXJpc2UobGlnaHRQRHMgPSBzdW0obGlnaHQqUE9QVUxBVElPTiwgbmEucm0gPSBUKSwNCiAgICAgICAgICAgIG1lZGl1bVBEcyA9IHN1bShtZWRpdW0qUE9QVUxBVElPTiwgbmEucm0gPSBUKSwNCiAgICAgICAgICAgIGhlYXZ5UERzID0gc3VtKGhlYXZ5KlBPUFVMQVRJT04sIG5hLnJtID0gVCkNCiAgKSAlPiUNCiAgZ2F0aGVyKGxpZ2h0UERzLCBtZWRpdW1QRHMsIGhlYXZ5UERzLCBrZXkgPSBzbW9rZSwgdmFsdWUgPSBQZXJzb25EYXlzKSAlPiUNCiAgbXV0YXRlKHNtb2tlID0gZmFjdG9yKHggPSBzbW9rZSwgbGV2ZWxzID0gYygibGlnaHRQRHMiLCAibWVkaXVtUERzIiwgImhlYXZ5UERzIikpKSAlPiUNCiAgICAgICBnZ3Bsb3QoYWVzKA0KICAgICAgICAgeCA9IGRheSwNCiAgICAgICAgIHkgPSBQZXJzb25EYXlzLzEwMDAwMDAsIA0KICAgICAgICAgZmlsbCA9IHNtb2tlDQogICAgICAgKSkgKw0KICAgICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImRvZGdlIikgKyANCiAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJ5ZWxsb3ciLCJvcmFuZ2UiLCJyZWQiKSkgKw0KICAgICAgIHlsYWIoIk1pbGxpb25zIG9mIFBlcnNvbnMgRXhwb3NlZCIpICsgDQogICAgICAgeGxhYigiTm92IGRheSIpIA0KICAgIA0KICAgIA0KYGBgDQoNCldlIGNvdWxkIGFsc28gZm9jdXMgb24gdGhlIGVsZGVybHkgZXhwb3NlZCBvdmVyIHRoZSBzYW1lIHBlcmlvZA0KDQpgYGB7cn0NCmVsZGVybHlTbW9rZSAlPiUNCiAgZ3JvdXBfYnkoZGF5KSAlPiUNCiAgc3VtbWFyaXNlKGhlYXZ5VW5kZXI2NT0gc3VtKGhlYXZ5KihQT1BVTEFUSU9OLWVzdGltYXRlKSwgbmEucm0gPSBUKSwNCiAgICAgICAgICAgIGhlYXZ5T3ZlcjY1ID0gc3VtKGhlYXZ5KmVzdGltYXRlLCBuYS5ybSA9IFQpDQogICkgJT4lDQogIGdhdGhlcihoZWF2eVVuZGVyNjUsIGhlYXZ5T3ZlcjY1LCBrZXkgPSBzbW9rZSwgdmFsdWUgPSBQZXJzb25EYXlzKSAlPiUNCiAgbXV0YXRlKHNtb2tlID0gZmFjdG9yKHggPSBzbW9rZSwgbGV2ZWxzID0gYygiaGVhdnlVbmRlcjY1IiwgImhlYXZ5T3ZlcjY1IikpKSAlPiUNCiAgICAgICBnZ3Bsb3QoYWVzKA0KICAgICAgICAgeCA9IGRheSwNCiAgICAgICAgIHkgPSBQZXJzb25EYXlzLzEwMDAwMDAsIA0KICAgICAgICAgZmlsbCA9IHNtb2tlDQogICAgICAgKSkgKw0KICAgICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249InN0YWNrIikgKyANCiAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJwaW5rIiwicmVkIikpICsNCiAgICAgICB5bGFiKCJNaWxsaW9ucyBvZiBQZXJzb25zIEV4cG9zZWQiKSArIA0KICAgICAgIHhsYWIoIk5vdiBkYXkiKSANCiAgICANCiAgICANCmBgYA0KQW5vdGhlciByZWFsbHkgdXNlZnVsIHdheSB0byBsb29rIGF0IHRoZSBkYXRhIHdpbGwgYmUgb24gYSBtYXAuIEZvciB0aGlzIHdlJ2xsIHN1bW1hcml6ZSB0aGUgZGF0YSBieSBsb2NhdGlvbi4gQWxzbywgd2hlcmUgd2UgdXNlZCBgcGxvdGAgcHJldmlvdXNseSB0aGlzIHdhcyBmaW5lIGZvciBnZXR0aW5nIGEgc2Vuc2Ugb2YgdGhlIGluZm9ybWF0aW9uIGJ1dCBub3QgdmVyeSB1c2VmdWwgZm9yIGFjdHVhbGx5IGludGVyYWN0aW5nIHdpdGggaXQuIEhlcmUsIHdlJ2xsIGltcGxlbWVudCBhIHBhY2thZ2UgdGhhdCB1c2VzIHRoZSBqYXZhIHNjcmlwdCBsaWJyYXJ5IFtMZWFmbGV0XShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2xlYWZsZXQvKS4gVGhpcyB3aWxsIHByb2R1Y2UgYSBtYXAgdGhhdCBpcyBtdWNoIG1vcmUgZmFtaWxpYXIgdG8gd2ViIHVzZXJzLiANCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoImxlYWZsZXQiKQ0KbGlicmFyeShsZWFmbGV0KQ0KDQojIGZpcnN0IHN1bWFyaXplIHRoZSBkYXRhIHdlIHdhbnQgYnkgbG9jYXRpb24sIGFnZ3JlZ2F0aW5nIHBlcnNvbiBkYXlzIG9mIGV4cG9zdXJlIGJ5IHRyYWN0DQoNCm1hcERhdGEgPC0gZWxkZXJseVNtb2tlICU+JQ0KICBncm91cF9ieShTVEFURUZQLCBDT1VOVFlGUCwgVFJBQ1RDRSwgTEFUSVRVREUsIExPTkdJVFVERSkgJT4lDQogIHN1bW1hcmlzZShFeHBvc2VkUE9QID0gc3VtKGhlYXZ5KlBPUFVMQVRJT04sIG5hLnJtID0gVCksDQogICAgICAgICAgICBFeHBvc2VkUE9QNjUgPSBzdW0oaGVhdnkqZXN0aW1hdGUsIG5hLnJtID0gVCkNCiAgKQ0KDQojIGRlZmluZSB0aGUgcGFsZXR0ZSBmb3IgdGhlIHJlc3VsdHMgc2hvd2luZyBmdWxsIHBvcHVsYXRpb24gDQpwYWxQT1AgPC0gY29sb3JRdWFudGlsZShwYWxldHRlID0gIkJsdWVzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICBkb21haW4gPSBtYXBEYXRhJEV4cG9zZWRQT1AsIA0KICAgICAgICAgICAgICAgICAgICAgICAgbiA9IDMpDQoNCiMgZGVmaW5lIHRoZSBwYWxldHRlIHRvIHNob3cgdGhlIGVsZGVybHkgc2VnbWVudCBvZiB0aGUgcG9wdWxhdGlvbiANCnBhbDY1IDwtIGNvbG9yUXVhbnRpbGUocGFsZXR0ZSA9ICJSZWRzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICBkb21haW4gPSBtYXBEYXRhJEV4cG9zZWRQT1A2NSwgDQogICAgICAgICAgICAgICAgICAgICAgICBuID0gMykNCg0KDQojIGhlcmUgd2UgY3JlYXRlIHRoZSBMZWFmdCBtYXAgd2l0aCB0d28gbGF5ZXJzLCBvbmUgZm9yIGFsbCBhZ2VzIGFuZCBvbmUgZm9yIGVsZGVybHkNCiAgbWFwRGF0YSAlPiUNCiAgICBsZWFmbGV0KCkgJT4lDQogICAgICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRDYXJ0b0RCLlBvc2l0cm9uKSAlPiUgIyB0aGVzZSBhcmUgdGhlIHRpbGVzIGZvciB0aGUgYmFja2dyb3VuZCBvZiB0aGUgbWFwIC0gZmluZCBtb3JlIGhlcmUgaHR0cDovL2xlYWZsZXQtZXh0cmFzLmdpdGh1Yi5pby9sZWFmbGV0LXByb3ZpZGVycy9wcmV2aWV3L2luZGV4Lmh0bWwgDQogICAgICBhZGRDaXJjbGVNYXJrZXJzKCAjIGhlcmUgd2UgYWRkIHRoZSBmaXJzdCBsYXllciwgc2luY2Ugb3VyIGRhdGEgaGFzIGxhdCBhbmQgbG9uLCB3ZSBjYW4gYWRkIHRoZSB0YWJ1bGFyIGRhdGEgZGlyZWNldGx5IHRvIGEgbWFwDQogICAgICAgIHJhZGl1cyA9IDMsDQogICAgICAgIGNvbG9yID0gfiBwYWxQT1AoRXhwb3NlZFBPUCksDQogICAgICAgIHN0cm9rZSA9IEZBTFNFLA0KICAgICAgICBmaWxsT3BhY2l0eSA9IC4zLA0KICAgICAgICBsYWJlbCA9IH5FeHBvc2VkUE9QLCAjIGhlcmUgaXMgdGV4dCB0aGF0IHdpbGwgYXBwZWFyIG9uIG1vdXNlb3Zlcg0KICAgICAgICBwb3B1cCA9IH5FeHBvc2VkUE9QLCAjIHRoaXMgaXMgdGV4dCB0byBhcHBlYXIgb24gYSBjbGljaw0KICAgICAgICBncm91cCA9ICJBbGwgQWdlcyINCiAgICAgICkgJT4lICANCiAgICAgIGFkZENpcmNsZU1hcmtlcnMoICMgaGVyZSB3ZSBhZGQgdGhlIHNlY29uZCBsYXllciwgc2luY2Ugb3VyIGRhdGEgaGFzIGxhdCBhbmQgbG9uLCB3ZSBjYW4gYWRkIHRoZSB0YWJ1bGFyIGRhdGEgZGlyZWNldGx5IHRvIGEgbWFwDQogICAgICAgIHJhZGl1cyA9ICB+IEV4cG9zZWRQT1A2NS9tYXgobWFwRGF0YSRFeHBvc2VkUE9QNjUpKjEwLA0KICAgICAgICBjb2xvciA9IH4gcGFsNjUoRXhwb3NlZFBPUDY1KSwNCiAgICAgICAgc3Ryb2tlID0gRkFMU0UsDQogICAgICAgIGZpbGxPcGFjaXR5ID0gLjMsDQogICAgICAgIGxhYmVsID0gfnBhc3RlMChmb3JtYXQoRXhwb3NlZFBPUDY1LCBiaWcubWFyayA9ICIsIiksICIgb3ZlciA2NSBwZXJzb24tZGF5cyBvZiBleHBvc3VyZSBpbiB0aGUgV2VlayB0aGUgQ2FtcCBGaXJlIGJlZ2FuIiksDQogICAgICAgIHBvcHVwID0gfkV4cG9zZWRQT1A2NSwNCiAgICAgICAgZ3JvdXAgPSAiT3ZlciA2NSINCiAgICAgICkgJT4lICANCiAgICBhZGRMYXllcnNDb250cm9sKA0KICAgICAgICAgICAgICAgICAgICBiYXNlR3JvdXBzID0gIGMoIkFsbCBBZ2VzIiwiT3ZlciA2NSIpLA0KICAgICAgICAgICAgICAgICAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpDQogICAgICAgICAgICAgICAgKSAlPiUNCiAgICAgIHNldFZpZXcobGF0ID0gMzcuMDg1MjA2LCBsbmcgPSAtMTE5LjU0MDA4NSwgem9vbSA9IDYpICU+JSAjZGVmYXVsdHMgdGhlIHZpZXcgb2YgdGhlIG9yaWdpbmFsIG1hcCB0byBzaG93IHRoZSBlbnRpcmUgc3RhdGUNCiAgICAgIGFkZEVhc3lCdXR0b24oZWFzeUJ1dHRvbigNCiAgICAgICAgaWNvbj0iZmEtZ2xvYmUiLCB0aXRsZT0iWm9vbSB0byBTdGF0ZSIsDQogICAgICAgIG9uQ2xpY2s9SlMoImZ1bmN0aW9uKGJ0biwgbWFwKXsgbWFwLnNldFZpZXcoWzM3LjA4NTIwNiwgLTExOS41NDAwODVdLDYpOyB9IikpKSAjIGFkZHMgYSBidXR0b24gdG8gcmV0dXJuIHRvIHRoZSB3aG9sZSBzdGFldCB2aWV3DQogICAgDQoNCmBgYA0KIyMgUGxlYXNlIFt0YWtlIGEgbW9tZW50IHRvIHByb3ZpZGUgdXMgd2l0aCBmZWVkYmFja10oaHR0cHM6Ly9nb28uZ2wvZm9ybXMvaHBVQXB5MGdQOThPOFM1SjIpIG9uIHRoaXMgc2Vzc2lvbi4NCg0KQWxzbywgdHJ5IHNvbWUgbW9kaWZpY2F0aW9ucyBhbmQgZXhlcmNpc2VzIG9uIHlvdXIgb3duLiBNb2RpZnkgdGhlIGNvZGUgdG8gDQoqIGxvb2sgYXQgeW91ciBvd24gc3RhdGUNCiogYSBzcGVjaWZpYyBjb3VudHkNCiogb3RoZXIgdnVsbmVyYWJsZSBzZWdtZW50cyBvZiB0aGUgcG9wdWxhdGlvbg0KKiBvdGhlciBlcGlzb2RlcyBvZiB3aWxkZmlyZSBzbW9rZQ0KKiBtYXAgbW9yZSB0aGFuIHR3byBsYXllcnMgb24gJ2V4cG9zdXJlJw0K